package com.laviedesfons.jhmtasc.types.internals;

import com.laviedesfons.jhmtasc.annotations.binding.MType;
import com.laviedesfons.jhmtasc.exceptions.MException;
import com.laviedesfons.jhmtasc.metadata.MetaType;
import com.laviedesfons.jhmtasc.types.interfaces.GenericType;
import com.laviedesfons.jhmtasc.types.interfaces.InternalType;
import com.laviedesfons.jhmtasc.types.interfaces.ToArray;
import fr.x9c.cadmium.kernel.Block;
import fr.x9c.cadmium.kernel.Value;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 *
 * @author Romano
 */
@MType
public final class PList<T> implements ToArray, GenericType {

    private List<InternalType> values = new ArrayList<InternalType>();
    private MetaType clazz = null;

    public PList(){}
    public PList(MetaType cl) {
        clazz = cl;
    }

    public PList(Collection<InternalType> c) {
        values = new ArrayList<InternalType>(c);
    }

    @Override
    public void setParams(MetaType... cls) {
        if(cls == null || cls.length != 1){
            throw new IllegalArgumentException("PList accepts only one MetaType");
        }
        clazz = cls[0];
    }

    public int length() {
        return this.values.size();
    }

    public InternalType get(int idx) {
        if ((values instanceof ArrayList)
                && idx >= 0 && idx < this.values.size()) {
            return (InternalType) ((ArrayList) this.values).get(idx);
        }
        return null;
    }

    @Override
    public List<InternalType> values() {
        return values;
    }

    @Override
    public PList fromValue(Value v) throws MException {
        if (clazz != null) {
            values = new ArrayList();
            if (v.isBlock()
                    && v.asBlock().sizeValues() > 0) {

                while (v.isBlock() && v.asBlock().sizeValues() == 2) {
                    values.add(clazz.create().fromValue(v.asBlock().get(0)));
                    v = v.asBlock().get(1);
                }
            }
            return this;
        }
        throw new MException("unable to parse list, no MetaType provided");
    }

    @Override
    public Value toValue() {
        Value res = Value.EMPTY_LIST;

        InternalType[] valueArray = new InternalType[this.values.size()];
        valueArray = this.values.toArray(valueArray);

        for (int i = valueArray.length-1; i >= 0; i--) {
            final Block cons = Block.createBlock(Block.TAG_CONS,
                    valueArray[i].toValue(),
                    res);
            res = Value.createFromBlock(cons);
        }
        return res;
    }

    @Override
    public String toString() {
        return values.toString();
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final PList other = (PList) obj;
        if (this.values != other.values && (this.values == null || !this.values.equals(other.values))) {
            return false;
        }
        if (this.clazz != other.clazz && (this.clazz == null || !this.clazz.equals(other.clazz))) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 29 * hash + (this.values != null ? this.values.hashCode() : 0);
        hash = 29 * hash + (this.clazz != null ? this.clazz.hashCode() : 0);
        return hash;
    }
}
